(ns behaviors.t-lazy-evaluation-cases
  (:require [midje.sweet :refer :all]
            [midje.util
             [laziness :refer :all]
             [thread-safe-var-nesting :refer :all]]
            [midje.test-util :refer :all]))

(unfinished triple)

(defn triple-sequence [n]
  (map triple (range n)))

(fact "triple-sequence returns a sequence of triples"
  "basic case for lazy sequences"
  (triple-sequence 4) => [0 3 6 9]
  (provided
    (triple 0) => 0
    (triple 1) => 3
    (triple 2) => 6
    (triple 3) => 9))

(fact "lazy sequences embedded within other structures"
  [(triple-sequence 2) {:a (triple-sequence 3) }]
  => [  [0 3]
        {:a [0 3 6] }]
  (provided
    (triple 0) => 0
    (triple 1) => 3
    (triple 2) => 6))

;; These cases explore lazy sequences with side effects

(def output-device (atom []))

(defn triple [n] (* 3 n))

(defn output-triples [n]
  (swap! output-device #(concat % (triple-sequence n))))

(fact "output-triples appends to state"
  (reset! output-device [])
  (against-background
    (triple 0) => 0
    (triple 1) => 3
    (triple 2) => 6
    (triple 3) => 9)
  (output-triples 4)
  @output-device => [0 3 6 9])

(defn doubly-output-triples [n]
  (let [triples (triple-sequence n)]
    (swap! output-device #(concat % triples))
    triples))

(fact "doubly-output-triples appends to state and returns value"
  (reset! output-device [])
  (against-background
    (triple 0) => 0
    (triple 1) => 3
    (triple 2) => 6
    (triple 3) => 9)
  (output-triples 4) => [0 3 6 9]
  @output-device => [0 3 6 9])

(fact "a variant way of doing the same thing"
  (reset! output-device [])
  (output-triples 4) => [0 3 6 9]
  (provided
    (triple 0) => 0
    (triple 1) => 3
    (triple 2) => 6
    (triple 3) => 9)
  @output-device => [0 3 6 9])

;; Exceptions generated by realizing lazy sequence.

(unfinished exploder)

(fact "exceptions are forced out in good time"
  (exploder) => (throws Error)
  (map exploder [1 2 3]) => (throws Error))

(unfinished process)
(defn pre-process [n] 5)

(defn baz []
  (map pre-process [1]))

(fact "mocking inside lazy seqs works: used to fail due to subtle bug"
  (process (baz)) => :done
  (provided
    (pre-process 1) => 5
    (process [5]) => :done))
